<?php
// Tim'Roster
// Copyright (c) 2011 Nicolas Nallet <aspgic2@gmail.com>
//
// This file is part of Tim'Roster.
//
// Tim'Roster is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Tim'Roster is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA

$xml_config = simplexml_load_file('config.xml');

include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/class/Material_Item.php');
include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/class/Crafted_Item.php');
include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/class/Enum/Enum_Ecosystem.php');
include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/class/Enum/Enum_Item_Race.php');
include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/data_access/item_DA.php');
include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/data_access/item_transaction_DA.php');
include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/technical_class/language/language.php');


class Guild_Hall_Room {

    private $item_transaction_DA;
    private $item_DA;

    /**
     * (PHP 5)<br/>
     * Construct a Guild_Hall_Room instance
     * @param <type> $guild_id
     */
    public function Guild_Hall_Room($guild_id){
        $this->guild_id = $guild_id;
        $this->item_transaction_DA = new item_transaction_DA();
        $this->item_DA = new item_DA();
    }


    private $guild_id;
    /**
     * (PHP 5)<br/>
     * Get guild hall's guild id
     * @return int
     */
    public function get_guild_id(){
        return $this->guild_id;
    }

    private $transaction_log;
    /**
     * (PHP 5)<br/>
     * Get guild hall transaction log
     * @return array of Transaction
     */
    public function get_transaction_log(){
        return $this->transaction_log;
    }

    private $current_content;
    /**
     * (PHP 5)<br/>
     * Get guild hall current content 
     * @return array of Item_Base
     */
    public function get_current_content(){
        return $this->current_content;
    }

    /**
     * (PHP 5)<br/>
     * Get items from specific type
     * @param int $item_type member of Enum_Item_Type
     * @return array
     */
    public function get_current_content_type_items($item_type){
        $result = array();

        for ($i = 0; $i < count($this->current_content); $i++) {
            if ($this->current_content[$i]->get_item_type_id() == $item_type){
                $result[] = $this->current_content[$i];
            }
        }

        return $result;
    }

    private $new_content;


    /** Xml Data Loading Functions **/

    /**
     * (PHP 5)<br/>
     * Load new guild hall content 
     * @param SimpleXMLElement $xml
     */
    public function load_new_content(SimpleXMLElement $xml){
        $item_list = $xml->xpath('/guild/room/*');

        if (isset($item_list)){
             foreach ($item_list as $item){
                if ((substr($item, 0,1) == 'm')  || ($item == 'ixpca01.sitem')) {
                    $this->add_material_item_from_xml($item);
                }
                else {
                    $this->add_crafted_item_from_xml($item);  
                }
             }
         }
    }

    /**
     * (PHP 5)<br/>
     * Add Material_Item in guild hall new content
     * @param SimpleXMLElement $item
     */
    private function add_material_item_from_xml(SimpleXMLElement $item){

        $attributes = $item->attributes();

        $qty = isset($attributes['s']) ? intval($attributes['s']) : 1;

        $qlt = intval($attributes['q']);
        $ecosystem = Enum_Ecosystem::convert_api_code_to_id(substr($item[0], 8, 1));

        $code = (string)$item[0];

        $mi = new Material_Item($this->guild_id, null, $code, Language::get_instance()->get_label(substr($code, 0,strpos($code,'.sitem'))),
                $qlt, $qty, $ecosystem, null);


        //ryzom maxstack is 999, but to simplify the code we make stack bigger than 999
        $index = $this->find_item_new_content($mi);

        if (isset($index)){
           $this->new_content[$index]->set_quantity($this->new_content[$index]->get_quantity() + $qty);
        }
        else {
            $this->new_content[] = $mi;
        }
    }

    /**
     * (PHP 5)<br/>
     * Add Crafted_Item in guild hall new content 
     * @param SimpleXMLElement $item
     */
    private function add_crafted_item_from_xml(SimpleXMLElement $item){

        $attributes = $item->attributes();

        //quality
        $qlt = intval($attributes['q']);

        //quantity
        $qty = isset($attributes['s']) ? intval($attributes['s']) : 1;

        //skin
        $skin = 1;
        if (strpos($item[0],'_2.') == true) $skin = 2;
        if (strpos($item[0],'_3.') == true) $skin = 3;

        //color
       $color = (isset($attributes['c']) ? intval($attributes['c']) : null);
        
        //item class
        $item_class = isset($attributes['e']) ? Enum_Item_Class::convert_api_code_to_id($attributes['e'][0]) : Enum_Item_Class::BASE_ITEM_CLASS_ID;

        //bonus
        $hp_bonus = isset($attributes['hpb']) ? intval($attributes['hpb']) : null;
        $sap_bonus = isset($attributes['sab']) ? intval($attributes['sab']) : null;
        $stam_bonus = isset($attributes['stb']) ? intval($attributes['stb']) : null;
        $focus_bonus = isset($attributes['fob']) ? intval($attributes['fob']) : null;

        //healpoint
        $healpoint = isset($attributes['hp']) ? intval($attributes['hp']) : null;

        //durability
        $durability = isset($attributes['dur']) ? intval($attributes['dur']) : null;


        //sapload
        $sapload = null;
        $current_sapload = null;
        if (isset($attributes['sl'])) {
            $sapload = intval($attributes['sl']);
            $current_sapload = 0;
        }

        //current_sapload
        if (isset($attributes['csl'])) $current_sapload = intval($attributes['csl']);

        //enchantement
        $enchant = isset($attributes['sap']) ? (intval($attributes['sap']) + 1) : null;

        //hit_rate
        $hit_rate = isset($attributes['hr']) ? intval($attributes['hr']) : null;

        //range
        $range = isset($attributes['r']) ? (round(intval($attributes['r']) / 6,0)) : null;

        $destroyed = isset($attributes['destroyed ']) ? intval($attributes['destroyed']) : null;

        //weapon and armor stats
        $dodge_modifier = null;
        $parry_modifier = null;
        $adv_dodge_modifier = null;
        $adv_parry_modifier = null;

        $protec_factor = null;
        $max_slash_protec = null;
        $max_pierce_protec = null;
        $max_blunt_protec = null;

        //if the item is an armor (only armor have a protection factor value
        if (isset($attributes['pf']))
        {
            $protec_factor = 0;
            $max_slash_protec = 0;
            $max_pierce_protec = 0;
            $max_blunt_protec = 0;
        }
        
        if (isset($attributes['pf'])) $protec_factor = intval($attributes['pf']);
        if (isset($attributes['msp'])) $max_slash_protec = intval($attributes['msp']);
        if (isset($attributes['mbp'])) $max_blunt_protec = intval($attributes['mbp']);
        if (isset($attributes['mpp'])) $max_pierce_protec = intval($attributes['mpp']);

        //if one modifier (dodge or parry) has a value, all modifier get the default value 0. Because
        //the API don't send the value if it's 0
        if (isset($protec_factor))
        {
            $dodge_modifier = 0;
            $parry_modifier = 0;
        }

        if ($range != 0 || $hit_rate != 0)
        {
            $dodge_modifier = 0;
            $parry_modifier = 0;
            $adv_dodge_modifier = 0;
            $adv_parry_modifier = 0;
        }

        if (isset($attributes['dm'])) $dodge_modifier = intval($attributes['dm']);
        if (isset($attributes['pm'])) $parry_modifier = intval($attributes['pm']);
        if (isset($attributes['adm'])) $adv_dodge_modifier = intval($attributes['adm']);
        if (isset($attributes['apm'])) $adv_parry_modifier = intval($attributes['apm']);


        $code = (string)$item[0];

        $ci = new Crafted_Item($this->guild_id, null, $code, Language::get_instance()->get_label(substr($code, 0,strpos($code,'.sitem'))), $qlt, $qty,
                $item_class, $skin, $color, $healpoint, $durability,
                $sapload, $current_sapload, $enchant, $hit_rate, $range,
                $dodge_modifier, $parry_modifier, $adv_dodge_modifier, $adv_parry_modifier, $protec_factor,
                $max_slash_protec, $max_pierce_protec, $max_blunt_protec, $destroyed,
                $hp_bonus, $sap_bonus, $stam_bonus, $focus_bonus, null);

        //ryzom maxstack is 999, but to simplify the code we make stack bigger than 999
        $index = $this->find_item_new_content($ci);        
        if (isset($index)){
            $this->new_content[$index]->set_quantity($this->new_content[$index]->get_quantity() + $qty);
        }
        else {
            $this->new_content[] = $ci;
        }
    }

    /**
     * (PHP 5)<br/>
     * Check if an item exists in guild hall new content
     * @param Item_Base $item
     * @return bool
     */
    private function find_item_new_content($item){
        if (count($this->new_content) > 0){

            for ($i = 0; $i < count($this->new_content); $i++) {
                if ($item->is_the_same_item($this->new_content[$i])){
                    return $i;
                }
            }
        }

        return null;
    }

    
    /** Database Data Loading Function **/

    /**
     * (PHP 5)<br/>
     * Get guild hall current content from database
     */
    public function load_current_content(){

        $item_list_mi = $this->item_DA->get_guild_material_item($this->guild_id);

        for ($i = 0; $i < count($item_list_mi); $i++) {
            $this->current_content[] = $item_list_mi[$i];
        }
        
        $item_list_ci = $this->item_DA->get_guild_crafted_item($this->guild_id);

        for ($i = 0; $i < count($item_list_ci); $i++) {
            $this->current_content[] = $item_list_ci[$i];
        }
    }

    /**
     * (PHP 5)<br/>
     * Get guild hall transaction log
     */
    public function load_transaction_log(){

        //get the 10 last transaction list
        $this->transaction_log = $this->item_transaction_DA->get_transaction_list(10, $this->get_guild_id());

    }



    /** Content Comparaison Functions **/ 

    /**
     * (PHP 5)<br/>
     * Update guild hall content. It compares $this->current_content and $this->new_content. Detect differences and update database
     * @param bool $is_guild_init if it's a guild init, the trasaction won't be logged in the transaction book
     * @return Item_Transaction
     */
   public function update_guild_hall_room_content($is_guild_init) {

        //create transaction in DB
        $transaction_id = $this->item_transaction_DA->insert_transaction_list(date('Y-m-d H:i:s',time()), $this->get_guild_id());

        $transaction_list = array();

        for ($i = 0; $i < count($this->current_content); $i++) {
            $room[] = new Comparaison_Item($this->current_content[$i]);
        }

        foreach($this->new_content as $item){
            $index = null;

            if (isset($room)) { $index = $this->find_item_in_list($item, $room); }

            if (!isset($index)){
                $trans = new Item_Transaction($item, Item_Transaction::TRANSACTION_TYPE_DEPOSIT_ID, $transaction_id);
                $transaction_list[] = $trans;

                $this->add_item_in_room($trans, $is_guild_init);
//                echo '1) DEPOSIT code : '.$item->get_code().' quantity : '.$item->get_quantity().'<br>';
            }
            else {
                $new_qty = 0;
                if ($item->get_quantity() != $room[$index]->get_item()->get_quantity()){
                    if ($item->get_quantity() > $room[$index]->get_item()->get_quantity()){
                        $trans = new Item_Transaction($item, Item_Transaction::TRANSACTION_TYPE_DEPOSIT_ID, $transaction_id);
                        $new_qty = $item->get_quantity();

//                        echo '2) DEPOSIT code : '.$item->get_code().' quantity : '.($item->get_quantity() - $room[$index]->get_item()->get_quantity()).'<br>';
                        $trans->get_item()->set_quantity($item->get_quantity() - $room[$index]->get_item()->get_quantity());
                        $transaction_list[] = $trans;
                    }
                    else {
                        if ($item->get_quantity() < $room[$index]->get_item()->get_quantity()){
                            $trans = new Item_Transaction($item, Item_Transaction::TRANSACTION_TYPE_WITHDRAWAL_ID, $transaction_id);
                            $new_qty = $item->get_quantity();

//                            echo '3) WITHDRAWAL code : '.$item->get_code().' quantity : '.($room[$index]->get_item()->get_quantity() - $item->get_quantity()).' db id : '.$room[$index]->get_item()->get_id().'<br>';
                            $trans->get_item()->set_quantity($room[$index]->get_item()->get_quantity() - $item->get_quantity());
                            $transaction_list[] = $trans;

                        }
                    }

                    $this->update_item_quantity($room[$index]->get_item()->get_id(), $new_qty, $trans->get_item()->get_item_type_id(), $trans);
                }

                $room[$index]->is_processed();
            }
        }

        if (isset($room)) {
            foreach($room as $compaison_item){
                //the other items in $room are those which were completely withdrawal
                if (!$compaison_item->get_is_processed()) {
                    $trans = new Item_Transaction($compaison_item->get_item(), Item_Transaction::TRANSACTION_TYPE_WITHDRAWAL_ID, $transaction_id);
                    $transaction_list[] = $trans;

                    $this->remove_item_from_room($compaison_item->get_item()->get_id(), $compaison_item->get_item()->get_item_type_id(), $trans) ;
//                    echo '4) WITHDRAWAL code : '.$compaison_item->get_item()->get_code().' quantity : '.$compaison_item->get_item()->get_quantity().'<br>';
                }
            }
        }

        return $transaction_list;
    }

    /**
     * (PHP 5)<br/>
     * Check if an item exists in list
     * @param Item_Base $item
     * @param array of Item_Base $item_list
     * @return int
     */
    private function find_item_in_list($item, $item_list){
        for ($key = 0; $key < count($item_list); $key++) {
//                echo 'code ITEM : '.$item->get_code().' qlt : '.$item->get_quality().' class : '.$item->get_item_class_id().'<br>';
//                echo 'code VAL : '.$val->get_item()->get_code().' qlt : '.$val->get_item()->get_quality().' class : '.$val->get_item()->get_item_class_id().'<br>';
            if ((!$item_list[$key]->get_is_processed())
                    && ($item->is_the_same_item($item_list[$key]->get_item()))) {
                return $key;
            }
        }

        return null;
    }


    /** Transaction Saving Functions **/

    /**
     * (PHP 5)<br/>
     * Remove item from the guild hall content
     * @param int $id
     * @param int $item_type
     * @param Item_Transaction $trans
     */
    private function remove_item_from_room($id, $item_type, Item_Transaction $trans){
        $this->item_DA->delete_item($id, $item_type);
        $this->item_transaction_DA->insert_item_transaction($trans);
    }

    /**
     * (PHP 5)<br/>
     * Add item in the guild hall content
     * @param Item_Transaction $trans
     * @param bool $is_guild_init if it's a guild init, the trasaction won't be logged in the transaction book
     */
    private function add_item_in_room(Item_Transaction $trans, $is_guild_init){
//        echo $trans->get_item()->get_item_type_id().' code : '.$trans->get_item()->get_code().'<br>';
//        if ($is_guild_init) { echo '$is_guild_init = true<br/>'; }
//        else { echo '$is_guild_init = false<br/>'; }

        $this->item_DA->insert_item($trans->get_item());

        if (!$is_guild_init) {
            $this->item_transaction_DA->insert_item_transaction($trans);
        }
    }

    /**
     * (PHP 5)<br/>
     * Update item quantity in the guild hall content
     * @param int $id
     * @param int $quantity
     * @param int $item_type
     * @param Item_Transaction $trans
     */
    private function update_item_quantity($id, $quantity, $item_type, Item_Transaction $trans){
        $this->item_DA->update_item_quantity($id, $quantity, $item_type);
        $this->item_transaction_DA->insert_item_transaction($trans);
//        echo 'update : id = '.$id.' item quatity = '.$quantity.' item_type : '.$item_type
//        .'<br>transaction type : '.$trans->get_transaction_type().' trans qty : '.$trans->get_item()->get_quantity().'<br>';
    }
}

class Transaction_List {

    private $item_transaction_DA;

    /**
     * (PHP 5)<br/>
     * Get the X last Transaction
     * @param int $count transaction count
     * @param int $guild_id 
     */
    public static function get_transaction_list(int $count, int $guild_id){
        $trans_DA = new item_transaction_DA();

        $list = $trans_DA->get_transaction_list($count, $guild_id);

        return $list;
    }

    public function Transaction_List($transaction_id, $transaction_date, $transaction_count){
        $this->transaction_id = $transaction_id;
        $this->transaction_date = $transaction_date;
        $this->transaction_count = $transaction_count;

        $this->item_transaction_DA = new item_transaction_DA();
    }

    /**
     * (PHP 5)<br/>
     * Get items which are desposited
     * @return array array of items desposited (class Item_Base)
     */
    public function get_deposited_item(){
        $item_array = array();

        $trans_array = $this->item_transaction_DA->get_item_transaction($this->transaction_id, Item_Transaction::TRANSACTION_TYPE_DEPOSIT_ID);

        for ($i = 0; $i < count($trans_array); $i++) {
            $item_array[] = $trans_array[$i]->get_item();
        }

        return $item_array;
    }

    /**
     * (PHP 5)<br/>
     * Get items which are withdrawaled
     * @return array array of items withdrawaled (class Item_Base)
     */
    public function get_withdrawaled_item(){
        $item_array = array();

        $trans_array =  $this->item_transaction_DA->get_item_transaction($this->transaction_id, Item_Transaction::TRANSACTION_TYPE_WITHDRAWAL_ID);

        for ($i = 0; $i < count($trans_array); $i++) {
            $item_array[] = $trans_array[$i]->get_item();
        }

        return $item_array;
    }

    private $transaction_id;
    /**
     * (PHP 5)<br/>
     * Get transaction id
     * @return int
     */
    public function get_transaction_id(){
        return $this->transaction_id;
    }


    private $transaction_date;
    /**
     * (PHP 5)<br/>
     * Get transaction date
     * @return date
     */
    public function get_transaction_date(){
        return $this->transaction_date;
    }

    private $transaction_count;
    /**
     * (PHP 5)<br/>
     * Get transaction count
     * @return int
     */
    public function get_transaction_count(){
        return $this->transaction_count;
    }

    
}

class Item_Transaction {
    const TRANSACTION_TYPE_DEPOSIT_ID = 0;
    const TRANSACTION_TYPE_WITHDRAWAL_ID = 1;

    /**
     * (PHP 5)<br/>
     * Construct a Transaction instance
     * @param Item_Base $item
     * @param int $transaction_type
     * @param int $transaction_id
     */
    public function Item_Transaction($item, $transaction_type, $transaction_list_id){
        $this->item = $item;
        $this->transaction_list_id = $transaction_list_id;
        $this->transaction_type = $transaction_type;
    }

    private $item;
    /**
     * (PHP 5)<br/>
     * Get transaction item
     * @return Item_Base
     */
    public function get_item(){
        return $this->item;
    }

    private $transaction_type;
    /**
     * (PHP 5)<br/>
     * Get transaction type
     * @return int
     */
    public function get_transaction_type(){
        return $this->transaction_type;
    }

    private $transaction_list_id;
    /**
     * (PHP 5)<br/>
     * Get transaction id
     * @return date
     */
    public function get_transaction_list_id(){
        return $this->transaction_list_id;
    }

}

class Comparaison_Item {

    /**
     * (PHP 5)<br/>
     * Construct a Comparaison_Item instance
     * @param Item_Base $item
     */
    public function Comparaison_Item($item){
        $this->item = $item;
        $this->is_processed = false;
    }

    private $item;
    /**
     * (PHP 5)<br/>
     * Get Comparaison_Item item
     * @return Item_Base
     */
    public function get_item(){
        return $this->item;
    }

    private $is_processed;
    /**
     * (PHP 5)<br/>
     * Get if the Comparaison_Item has been processed
     * @return bool
     */
    public function get_is_processed(){
        return $this->is_processed;
    }
    /**
     * (PHP 5)<br/>
     * Set $this->is_processed to true
     */
    public function is_processed(){
        $this->is_processed = true;
    }
}



?>